package com.dcivision.lucene;

import java.io.InputStream;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.document.Document;

import com.dcivision.dms.bean.DmsContent;
import com.dcivision.dms.bean.DmsDocument;
import com.dcivision.dms.bean.DmsDocumentIndexCache;
import com.dcivision.dms.bean.DmsVersion;
import com.dcivision.dms.core.DocumentRetrievalManager;
import com.dcivision.dms.dao.DmsDocumentDAObject;
import com.dcivision.dms.dao.DmsDocumentDetailDAObject;
import com.dcivision.dms.dao.DmsDocumentIndexCacheDAObject;
import com.dcivision.framework.DataSourceFactory;
import com.dcivision.framework.GlobalConstant;
import com.dcivision.framework.SessionContainer;

public class IndexAutoTask extends Thread {
  
  protected Log log = LogFactory.getLog(this.getClass().getName());
  
  private int bufferSize = 500;
  
  private long indexInterval = 30000; //30 S
  
  private boolean isRunning = false;
  
  public IndexAutoTask() {
  }
  
  public synchronized void run() {
    while (true) {
      if( ! isRunning ) {
        try{
          isRunning = true;
          this.indexDmsDocumentAction();
        }catch(Exception ignore){
          log.error(ignore, ignore);
        }finally {
          isRunning = false;
        }
      }
      try {
        wait(indexInterval);
      } catch(InterruptedException e) {
        log.warn("Index task is interrupted.");
      }
    }
  }
  
  /**
   * wake up run thread, and do index work.
   */
  public synchronized void indexImmediately() {
    if (isRunning) {
      log.debug("Calcel");
    }else {
      notifyAll();
    }
  }
  
  /**
   * Do index action, called by run.
   *
   */
  private void indexDmsDocumentAction()  {
    
    SessionContainer sessionContainer = null;
    Connection conn = null;
    IndexManager indexManager =null;
    try {
      sessionContainer = new SessionContainer();
      conn = DataSourceFactory.getConnection();
      indexManager = new IndexManager(sessionContainer, conn);
      
      DmsDocumentIndexCacheDAObject indexCacheDAO = new DmsDocumentIndexCacheDAObject(sessionContainer, conn);
      DmsDocumentDAObject dmsDocumentDAO = new DmsDocumentDAObject(sessionContainer, conn);
      DmsDocumentDetailDAObject dmsDocumentDetailDAO = new DmsDocumentDetailDAObject(sessionContainer, conn);
      
      while (true) {
        // get top records from database.
        List indexCaches = indexCacheDAO.getTopList(bufferSize, "ID");
        if (indexCaches.isEmpty()) {
          break;
        }
        
        
        // if list is not empty, index lucene
        Integer topID = null;
        
        // preload documents object from database, but delete action not need to load.
        List lstDmsDocumentIds = new ArrayList();
        for (int i=0; i<indexCaches.size(); i++) {
          DmsDocumentIndexCache indexCache = (DmsDocumentIndexCache)indexCaches.get(i);
          if (!DmsDocumentIndexCache.ACTION_MODE_DELETE.equals(indexCache.getActionMode())) {
            lstDmsDocumentIds.add(indexCache.getDmsDocumentID());
          }
          topID = indexCache.getID();
        }
        Map mapDmsDocuments = (lstDmsDocumentIds.isEmpty()) ? new HashMap() : dmsDocumentDAO.getMapByIDList(lstDmsDocumentIds);
        log.debug("Execute indexDmsDocumentAction. New cached files found. ");
        try {
          for (int i=0; i<indexCaches.size(); i++) {
            DmsDocumentIndexCache indexCache = (DmsDocumentIndexCache)indexCaches.get(i);
            Integer documentIDForLog = null;
            String operateForLog = null;
            try {
              DmsDocument dmsDocument = (DmsDocument)mapDmsDocuments.get(indexCache.getDmsDocumentID());
              // if can not get dms_document, then the document may be deleted, but delete action must skip it.
              if (dmsDocument==null && !DmsDocumentIndexCache.ACTION_MODE_DELETE.equals(indexCache.getActionMode())) {
                continue;
              }
              documentIDForLog = indexCache.getDmsDocumentID();
              operateForLog = indexCache.getActionMode();
              log.debug(" index document in indexDmsDocumentAction with documentID :" + documentIDForLog + " operate mode :" + operateForLog);
              // delete lucene.
              if (DmsDocumentIndexCache.ACTION_MODE_DELETE.equals(indexCache.getActionMode())) {
                if (DmsDocumentIndexCache.ACTION_TYPE_DELETE_SOFT.equals(indexCache.getActionType())) {
                  Document doc = indexManager.queryDocument(indexCache.getDmsDocumentID());
                  if (doc!=null) {
                    doc = indexManager.resetDocument(doc, new String[]{FieldNames.RECORD_STATUS}, new String[]{GlobalConstant.RECORD_STATUS_INACTIVE});
                  }
                  indexManager.deleteDmsDocument(indexCache.getDmsDocumentID());
                  indexManager.addDocument(doc);
                }else if (DmsDocumentIndexCache.ACTION_TYPE_DELETE_HARD.equals(indexCache.getActionType())) {
                  indexManager.deleteDmsDocument(indexCache.getDmsDocumentID());
                }
                // new insert 
              }else if (DmsDocumentIndexCache.ACTION_MODE_INSERT.equals(indexCache.getActionMode())) {
                indexManager.addDmsDocument(dmsDocument);
                // update lucene field.
              }else if (DmsDocumentIndexCache.ACTION_MODE_UPDATE.equals(indexCache.getActionMode())) {
                Document doc = indexManager.queryDocument(indexCache.getDmsDocumentID());
                if (doc!=null) {
                  if (DmsDocumentIndexCache.ACTION_TYPE_UPDATE_ALL.equals(indexCache.getActionType())) {
                    List dmsDocumentDetails = dmsDocumentDetailDAO.getListByDocumentID(dmsDocument.getID());
                    doc = indexManager.resetDocumentStandardFields(doc, dmsDocument);
                    doc = indexManager.resetDocumentDDFIndexFields(doc, dmsDocument);
                    doc = indexManager.resetDocumentUDFIndexFields(doc, dmsDocumentDetails);
                  }else if (DmsDocumentIndexCache.ACTION_TYPE_UPDATE_STANDARD.equals(indexCache.getActionType())) {
                    doc = indexManager.resetDocumentStandardFields(doc, dmsDocument);
                  }else if (DmsDocumentIndexCache.ACTION_TYPE_UPDATE_NAME.equals(indexCache.getActionType())) {
                    doc = indexManager.resetDocument(doc, new String[]{FieldNames.DOCUMENT_NAME, FieldNames.DOCUMENT_NAME_SORT}, new String[]{dmsDocument.getDocumentName(), dmsDocument.getDocumentName()});
                  }
                  indexManager.deleteDmsDocument(indexCache.getDmsDocumentID());
                  indexManager.addDocument(doc);
                }
                // copy lucene index
              }else if (DmsDocumentIndexCache.ACTION_MODE_COPY.equals(indexCache.getActionMode())) {
                Document doc = indexManager.queryDocument(indexCache.getSourceDocumentID());
                if (doc!=null) {
                  if (DmsDocumentIndexCache.ACTION_TYPE_UPDATE_ALL.equals(indexCache.getActionType())) {
                    List dmsDocumentDetails = dmsDocumentDetailDAO.getListByDocumentID(dmsDocument.getID());
                    doc = indexManager.resetDocumentStandardFields(doc, dmsDocument);
                    doc = indexManager.resetDocumentDDFIndexFields(doc, dmsDocument);
                    doc = indexManager.resetDocumentUDFIndexFields(doc, dmsDocumentDetails);
                  }else if (DmsDocumentIndexCache.ACTION_TYPE_UPDATE_STANDARD.equals(indexCache.getActionType())) {
                    doc = indexManager.resetDocumentStandardFields(doc, dmsDocument);
                  }
                  indexManager.addDocument(doc);
                }
                // move lucene index
              }else if (DmsDocumentIndexCache.ACTION_MODE_MOVE.equals(indexCache.getActionMode())) {
                Document doc = indexManager.queryDocument(indexCache.getSourceDocumentID());
                if (doc!=null) {
                  if (DmsDocumentIndexCache.ACTION_TYPE_UPDATE_ALL.equals(indexCache.getActionType())) {
                    List dmsDocumentDetails = dmsDocumentDetailDAO.getListByDocumentID(dmsDocument.getID());
                    doc = indexManager.resetDocumentStandardFields(doc, dmsDocument);
                    doc = indexManager.resetDocumentDDFIndexFields(doc, dmsDocument);
                    doc = indexManager.resetDocumentUDFIndexFields(doc, dmsDocumentDetails);
                  }else if (DmsDocumentIndexCache.ACTION_TYPE_UPDATE_STANDARD.equals(indexCache.getActionType())) {
                    doc = indexManager.resetDocumentStandardFields(doc, dmsDocument);
                  }
                  indexManager.deleteDmsDocument(indexCache.getSourceDocumentID());
                  indexManager.addDocument(doc);
                }
                // restore record.
              }else if (DmsDocumentIndexCache.ACTION_MODE_RESTORE.equals(indexCache.getActionMode())) {
                Document doc = indexManager.queryDocument(indexCache.getDmsDocumentID());
                if (doc!=null) {
                  doc = indexManager.resetDocument(doc, new String[]{FieldNames.RECORD_STATUS}, new String[]{GlobalConstant.RECORD_STATUS_ACTIVE});
                }
                indexManager.deleteDmsDocument(indexCache.getDmsDocumentID());
                indexManager.addDocument(doc);
              }else if(DmsDocumentIndexCache.ACTION_MODE_CHECKIN.equals(indexCache.getActionMode())){ 
                log.debug("Reindexing Lucene for check in action"); 
                Document doc = indexManager.queryDocument(indexCache.getDmsDocumentID()); 
                DocumentRetrievalManager retrievalManager = new DocumentRetrievalManager(sessionContainer, conn); 
                DmsVersion dmsVersion = retrievalManager.getTopVersionByDocumentID(indexCache.getDmsDocumentID()); 
                DmsContent dmsContent = retrievalManager.getContentByContentID(dmsVersion.getContentID()); 
                InputStream data = retrievalManager.getDataInputStreamByDocument(retrievalManager.getDocument(indexCache.getDmsDocumentID()), dmsVersion, dmsContent); 
                if (doc!=null) { 
                  doc = indexManager.resetDocumentStandardFields(doc, dmsDocument, null); 
                  doc = indexManager.resetDocumentContent(doc, data, dmsContent.getExt()); 
                  indexManager.deleteDmsDocument(indexCache.getDmsDocumentID()); 
                  indexManager.addDocument(doc); 
                }else { 
                  indexManager.addDmsDocument(dmsDocument, dmsContent, data, dmsDocument.getDocumentDetails()); 
                } 
              }else{ 
                throw new IllegalStateException("Unknown action type"); 
              } 
              
            }catch (OutOfMemoryError e) {
              log.error("Fail to index document("+indexCache.getDmsDocumentID()+") by out of memory error", e);
            }catch (Exception ex) {
              log.error("Error occur in IndexAutoTask with DocumentID:" + documentIDForLog + " OperateMode:" + operateForLog, ex);
            }
          }
          // delete cache records.
          indexCacheDAO.deleteTopList(topID);
          conn.commit();
          log.debug("Execute indexDmsDocumentAction. New cached files processing END. ");
        }catch (Exception ex) {
          conn.rollback();
          throw ex;
        }
      }
    }catch (Exception ex) {
      log.error(ex, ex);
    }finally {
      try {conn.close();} catch (Exception ignore) {}finally {conn = null;}
      try {indexManager.close();} catch (Exception ignore) {}finally {indexManager = null;}
    } 
  }
  
}
